"use strict";

let elasticsearch = require('elasticsearch');
let config = require('../config/config.js');
let EventEmitter = require('events').EventEmitter;
let debug = require('debug')('elasticSearch');
let util = require('util');
let __index = config.elasticSearch.index;

let client = new elasticsearch.Client({
    host: config.elasticSearch.host,
    //log: 'trace'
    requestTimeout: Infinity, // Tested
    keepAlive: true // Tested
});

function EsClient() {
    this.host = config.elasticSearch.host;
    this.index = __index;
    this.client = client;
    this.indexReady = false;
    this.readyType = [];
    this.enroll = {}; //{type:xxx,mapping:xxx}
}

util.inherits(EsClient, EventEmitter);


// ping the server
client.ping({
    requestTimeout: 40000,
    hello: "elasticsearch"
}, function (error) {
    if (error) {
        console.error('elasticsearch cluster is down!');
        throw new Error('elasticsearch cluster is down!');
    } else {
        debug('--------------ping es server, all is well');
        esClient.createIndexIfNecessary(__index);
    }
});

/**
 * check index , if not exists, create it
 * @param index
 */
EsClient.prototype.createIndexIfNecessary = function () {
    debug('--------------createIndexIfNecessary');
    if (this.indexReady) {
        return;
    }

    let self = this;
    client.indices.exists({
        index: __index
    }).then(function fullfil(body) { // create index if necessary
        //debug('fullfil', body);
        if (!body) {
            self.createIndex(__index);
        } else {
            self.afterIndexReady();
        }
    });
};

EsClient.prototype.createIndex = function () {
    let self = this;
    return client.indices.create({
        index: __index
    }).then(function fullfil(body) { // create index if necessary
        debug('fullfil', body);
        if (body.acknowledged) {
            self.afterIndexReady();
        }
    }, function reject(error) {
        debug('reject', error);
    });
};

EsClient.prototype.afterIndexReady = function () {
    debug('--------------afterIndexReady');
    this.indexReady = true;
    for (let type in this.enroll) {
        let mapping = this.enroll[type];
        this.createMappingIfNecessary(type, mapping);
    }
    this.emit('indexReady');
};

EsClient.prototype.afterTypeReady = function (type) {
    debug('--------------afterTypeReady:type=' + type);
    this.emit('typeReady', type);
    // add to ready
    if (this.readyType.indexOf(type) < 0)
        this.readyType.push(type);

    // remove from register
    delete this.enroll[type];
};

EsClient.prototype.createMappingIfNecessary = function (type, mapping) {
    debug('--------------createMappingIfNecessary');

    // check index ready or not
    if (!this.indexReady) {
        debug('--------------createMappingIfNecessary: wait index ready ');
        this.registerType(type, mapping);
        return;
    }

    // check type ready or not
    if (this.readyType.indexOf(type) > 0) {
        debug('--------------createMappingIfNecessary: this type already ready');
        self.afterTypeReady(type);
        return;
    }

    let self = this;
    client.indices.getMapping({
        index: __index,
        type: type
    }).then(function fullfil(body) { // create index if necessary
        //debug('fullfil', body);
        let index = body[__index];
        let exists = !!index && !!index.mappings && !!index.mappings[type];

        if (!exists) {
            self.createMapping(type, mapping);
        } else {
            debug('--------------createMappingIfNecessary: this type exist ready');
            self.afterTypeReady(type);
        }
    }, function reject(error) {
        console.error(error);
    });
};

EsClient.prototype.createMapping = function (type, mapping) {
    debug('--------------createMapping:type=' + type);
    let self = this;
    client.indices.putMapping({
        index: __index,
        type: type,
        ignoreConflicts: true,
        body: mapping
    }).then(function (body) {
        debug(body);
        self.afterTypeReady(type);
    }, function reject(error) {
        console.error(error);
    });
};

EsClient.prototype.registerType = function (type, mapping) {
    debug('--------------registerType:type=' + type);
    if (this.readyType.indexOf(type) < 0 && !this.enroll[type]) {
        this.enroll[type] = mapping;
    }
};

EsClient.prototype.dropTypeReady = function (type) {
    let self = this;
    let index = self.readyType.indexOf(type);
    if (index >= 0) {
        self.readyType.splice(index, 1);
    }
};

// init index
let esClient = new EsClient();
//esClient.createIndexIfNecessary(__index);
module.exports = esClient;
